package org.orbis.index;

import org.msgpack.MessagePackObject;
import org.msgpack.object.ArrayType;
import org.msgpack.object.IntegerType;
import org.msgpack.object.RawType;
import org.orbis.structure.ContentInfo;

public class IndexingPackage{
	private int cluster;
	private long ID;
	private long clock;
	private boolean isACK;
	private String[] content;
	private String[] appendix;
	private boolean[] binFlags;
	private int[] intFlags;
	private long[] longFlags;
	private float[] floatFlags;

	// Use it normally, from content information
	public IndexingPackage(ContentInfo contentInfo){
		this.content = new String[contentInfo.contentsNum()];
		this.appendix = new String[contentInfo.appendixNum()];
		this.binFlags = new boolean[contentInfo.binaryFlagNum()];
		this.intFlags = new int[contentInfo.integerFlagNum()];
		this.longFlags = new long[contentInfo.longFlagNum()];
		this.floatFlags = new float[contentInfo.floatFlagNum()];
		this.isACK = false;
		this.clock = System.currentTimeMillis();
	}

	// Use it for reverting from log file
	public IndexingPackage(int contentLen, int appendixLen, int binaryFlagLen, int intFlagLen,
		int longFlagLen, int floatFlagLen){
		this(0, contentLen, appendixLen, binaryFlagLen, intFlagLen, longFlagLen, floatFlagLen);
	}

	// Use locally to make package from messagepack message
	public IndexingPackage(long ID, int contentLen, int appendixLen, int binaryFlagLen, int intFlagLen,
		int longFlagLen, int floatFlagLen){
		this.ID = ID;
		this.content = new String[contentLen];
		this.appendix = new String[appendixLen];
		this.binFlags = new boolean[binaryFlagLen];
		this.intFlags = new int[intFlagLen];
		this.longFlags = new long[longFlagLen];
		this.floatFlags = new float[floatFlagLen];
	}

	public static final int HEADER = 'p' << 24 | 'c' << 16 | 'o' << 8 | 'n';
	
	public ArrayType toPackageContent(){
		int i, subtotal;
		int contentLength = content.length;
		int appendixLength = appendix.length;
		MessagePackObject[] object = new MessagePackObject[contentLength + appendixLength + 5];
		// Header: 'pcon'
		object[0] = IntegerType.create(HEADER);
		object[1] = IntegerType.create(ID);
		object[2] = IntegerType.create(cluster);
		object[3] = IntegerType.create(contentLength);
		object[4] = IntegerType.create(appendixLength);
		for(i = 0; i < contentLength; i++)
			object[5 + i] = RawType.create(content[i]);
		subtotal = contentLength + 5;
		for(i = 0; i < appendixLength; i++)
			object[subtotal + i] = RawType.create(appendix[i]);

		return ArrayType.create(object);
	}

	public static IndexingPackage fromPackageContent(ArrayType packed){
		MessagePackObject[] object = packed.asArray();

		int header = object[0].asInt();
		if(header != HEADER) return null;
		int contentLength = object[3].asInt();
		int appendixLength = object[4].asInt();
		int i, subtotal = 5;
		IndexingPackage pkg = new IndexingPackage(contentLength, appendixLength, 0, 0, 0, 0);

		pkg.setID(object[1].asLong());
		pkg.setCluster(object[2].asInt());
		for(i = 0; i < contentLength; i++){
			pkg.setContent(object[subtotal + i].asString(), i);
		}
		subtotal += contentLength;
		for(i = 0; i < appendixLength; i++){
			pkg.setAppendix(object[subtotal + i].asString(), i);
		}

		return pkg;
	}

	public IndexingPackage setCluster(int index){
		this.cluster = index;
		return this;
	}

	public IndexingPackage setID(long ID){
		this.ID = ID;
		return this;
	}

	public IndexingPackage setContent(String content, int index){
		if(index >= this.content.length) return this;
		this.content[index] = content;
		return this;
	}

	public IndexingPackage setAppendix(String appendix, int index){
		if(index >= this.appendix.length) return this;
		this.appendix[index] = appendix;
		return this;
	}

	public IndexingPackage setBinaryFlag(boolean flag, int index){
		if(index >= this.binFlags.length) return this;
		this.binFlags[index] = flag;
		return this;
	}

	public IndexingPackage setIntFlag(int flag, int index){
		if(index >= this.intFlags.length) return this;
		this.intFlags[index] = flag;
		return this;
	}

	public IndexingPackage setLongFlag(long flag, int index){
		if(index >= this.longFlags.length) return this;
		this.longFlags[index] = flag;
		return this;
	}

	public IndexingPackage setFloatFlag(float flag, int index){
		if(index >= this.floatFlags.length) return this;
		this.floatFlags[index] = flag;
		return this;
	}

	public int cluster(){
		return cluster;
	}

	public long ID(){
		return ID;
	}

	public String content(int index){
		return this.content[index];
	}

	public String[] contents(){
		return this.content;
	}

	public int contentLength(){
		return this.content.length;
	}

	public String appendix(int index){
		return this.appendix[index];
	}

	public String[] appendices(){
		return this.appendix;
	}

	public int appendixLength(){
		return this.appendix.length;
	}

	public boolean[] binaryFlags(){
		return this.binFlags;
	}

	public boolean binaryFlag(int index){
		return this.binFlags[index];
	}

	public boolean isACK(){
		return isACK;
	}

	public int binaryFlagLength(){
		return this.binFlags.length;
	}

	public int intFlag(int index){
		return this.intFlags[index];
	}

	public int[] intFlags(){
		return this.intFlags;
	}

	public int intFlagLength(){
		return this.intFlags.length;
	}

	public long longFlag(int index){
		return this.longFlags[index];
	}

	public long[] longFlags(){
		return this.longFlags;
	}

	public int longFlagLength(){
		return this.longFlags.length;
	}

	public float floatFlag(int index){
		return this.floatFlags[index];
	}

	public float[] floatFlags(){
		return this.floatFlags;
	}

	public int floatFlagLength(){
		return this.floatFlags.length;
	}

	public void ACK(){
		this.isACK = true;
	}

	public long responseTime(){
		return System.currentTimeMillis() - clock;
	}
}
